package com.cyanite.playlv2;

import com.cyanite.playlv2.util.MenuUtils;
import com.cyanite.playlv2.util.NotificationsUtil;
import com.cyanite.playlv2.util.RemoteResourceManager;
import com.cyanite.playlv2.util.UiUtil;
import com.cyanite.playlv2.util.UserUtils;
import com.cyanite.playlv2.widget.PhotoStrip;
import com.cyanite.playlv2.widget.UserContactAdapter;
import com.cyanite.playlv2api.PlayLv2Api;
import com.cyanite.playlv2api.error.PlayLv2ApiException;
import com.cyanite.playlv2api.types.Response;
import com.cyanite.playlv2api.types.User;
import com.cyanite.playlv2.R;

import android.app.Activity;
import android.app.AlertDialog;
import android.app.Dialog;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.DialogInterface;
import android.content.Intent;
import android.content.IntentFilter;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Typeface;
import android.net.Uri;
import android.os.AsyncTask;
import android.os.Bundle;
import android.os.Handler;
import android.text.Spannable;
import android.text.SpannableString;
import android.text.TextUtils;
import android.text.style.CharacterStyle;
import android.text.style.StyleSpan;
import android.view.KeyEvent;
import android.view.Menu;
import android.view.MenuItem;
import android.view.View;
import android.view.View.OnClickListener;
import android.view.Window;
import android.widget.Button;
import android.widget.ImageView;
import android.widget.TextView;
import android.widget.Toast;

import java.io.IOException;
import java.util.Observable;
import java.util.Observer;

public class UserDetailsActivity extends Activity {

    private static final int ACTIVITY_REQUEST_CODE_PINGS = 815;
    private static final int ACTIVITY_REQUEST_CODE_FETCH_IMAGE = 816;
    private static final int ACTIVITY_REQUEST_CODE_VIEW_AND_SET_IMAGE = 817;
    private static final int ACTIVITY_FRIEND_REQUEST = 819;
    private static final int ACTIVITY_NEW_MESSAGE = 820;

    public static final String EXTRA_USER_PARCEL = PlayLv2.PACKAGE_NAME
        + ".UserDetailsActivity.EXTRA_USER_PARCEL";
    public static final String EXTRA_USER_ID = PlayLv2.PACKAGE_NAME
        + ".UserDetailsActivity.EXTRA_USER_ID";
    public static final String EXTRA_SHOW_ADD_FRIEND_OPTIONS = PlayLv2.PACKAGE_NAME
        + ".UserDetailsActivity.EXTRA_SHOW_ADD_FRIEND_OPTIONS";
    public static final String EXTRA_MAIN = PlayLv2.PACKAGE_NAME
        + ".UserDetailsActivity.EXTRA_MAIN";

    private static final int LOAD_TYPE_USER_NONE = 0;
    private static final int LOAD_TYPE_USER_ID = 1;
    private static final int LOAD_TYPE_USER_PARTIAL = 2;
    private static final int LOAD_TYPE_USER_FULL = 3;

    private static final int MENU_REFRESH = 0;
    private static final int MENU_CONTACT = 1;
    private static final int MENU_PINGS = 2;
    private static final int DIALOG_CONTACTS = 0;

    private StateHolder mStateHolder;
    private RemoteResourceManager mRrm;
    private RemoteResourceManagerObserver mResourcesObserver;
    private Handler mHandler;

    private BroadcastReceiver mLoggedOutReceiver = new BroadcastReceiver() {
        @Override
        public void onReceive(Context context, Intent intent) {
            finish();
        }
    };

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        requestWindowFeature(Window.FEATURE_INDETERMINATE_PROGRESS);
        setContentView(R.layout.user_details_activity);
        registerReceiver(mLoggedOutReceiver, new IntentFilter(PlayLv2.INTENT_ACTION_LOGGED_OUT));

        mStateHolder = new StateHolder();
        if (getIntent().hasExtra(EXTRA_USER_PARCEL)) {
            User user = getIntent().getExtras().getParcelable(EXTRA_USER_PARCEL);
            mStateHolder.setUser(user);
            mStateHolder.setLoadType(LOAD_TYPE_USER_PARTIAL);
        } else if (getIntent().hasExtra(EXTRA_USER_ID)) {
            User user = new User();
            user.setId(getIntent().getExtras().getString(EXTRA_USER_ID));
            mStateHolder.setUser(user);
            mStateHolder.setLoadType(LOAD_TYPE_USER_ID);
        } else {
            User user = new User();
            user.setId(null);
            mStateHolder.setUser(user);
            mStateHolder.setLoadType(LOAD_TYPE_USER_ID);
        }

        if (getIntent().hasExtra(EXTRA_MAIN))   {
            mStateHolder.setIsMainPage(getIntent().getExtras().getInt(EXTRA_MAIN)==1?true:false);
        }

        mStateHolder.setIsLoggedInUser(
          mStateHolder.getUser().getId() == null ||
          mStateHolder.getUser().getId().equals(((PlayLv2) getApplication()).getUserId()));

        mHandler = new Handler();
        mRrm = ((PlayLv2) getApplication()).getRemoteResourceManager();
        mResourcesObserver = new RemoteResourceManagerObserver();
        mRrm.addObserver(mResourcesObserver);

        ensureUi();

        if (mStateHolder.getLoadType() != LOAD_TYPE_USER_FULL &&
           !mStateHolder.getIsRunningUserDetailsTask() &&
           !mStateHolder.getRanOnce()) {
            mStateHolder.startTaskUserDetails(this, mStateHolder.getUser().getId());
        }
    }

    @Override
    public void onPause() {
        super.onPause();

        if (isFinishing()) {
            mStateHolder.cancelTasks();
            mHandler.removeCallbacks(mRunnableUpdateUserPhoto);
            mRrm.deleteObserver(mResourcesObserver);
        }
    }

    @Override
    protected void onDestroy() {
        super.onDestroy();

        unregisterReceiver(mLoggedOutReceiver);
    }

    @Override
    public boolean onCreateOptionsMenu(Menu menu) {
        super.onCreateOptionsMenu(menu);

        menu.add(Menu.NONE, MENU_REFRESH, Menu.NONE, R.string.refresh)
            .setIcon(R.drawable.ic_menu_refresh);

        if (mStateHolder.getIsLoggedInUser()) {
            MenuUtils.addPreferencesToMenu(this, menu);
        } else {
            menu.add(Menu.NONE, MENU_CONTACT, Menu.NONE, R.string.user_details_activity_friends_menu_contact)
                .setIcon(R.drawable.ic_menu_user_contact);

            if (UserUtils.isFriend(mStateHolder.getUser())) {
                menu.add(Menu.NONE, MENU_PINGS, Menu.NONE, R.string.user_details_activity_friends_menu_pings)
                    .setIcon(android.R.drawable.ic_menu_rotate);
            }
        }
        return true;
    }

    @Override
    public boolean onPrepareOptionsMenu(Menu menu) {
        User user = mStateHolder.getUser();

        MenuItem refresh = menu.findItem(MENU_REFRESH);
        MenuItem contact = menu.findItem(MENU_CONTACT);
        MenuItem pings   = menu.findItem(MENU_PINGS);
        if (!mStateHolder.getIsRunningUserDetailsTask()) {
            refresh.setEnabled(true);
            if (contact != null) {
                boolean contactEnabled =
                    !TextUtils.isEmpty(user.getEmail()) ||
                    !TextUtils.isEmpty(user.getPhone());
                contact.setEnabled(contactEnabled);
            }
            if (pings != null) {
                pings.setEnabled(true);
            }
        } else {
            refresh.setEnabled(false);
            if (contact != null) {
                contact.setEnabled(false);
            }
            if (pings != null) {
                pings.setEnabled(false);
            }
        }

        return super.onPrepareOptionsMenu(menu);
    }

    @Override
    public boolean onOptionsItemSelected(MenuItem item) {
        switch (item.getItemId()) {
            case MENU_REFRESH:
                mStateHolder.startTaskUserDetails(this, mStateHolder.getUser().getId());
                return true;
            case MENU_CONTACT:
                showDialog(DIALOG_CONTACTS);
                return true;
            case MENU_PINGS:
                Intent intentPings = new Intent(this, UserDetailsPingsActivity.class);
                intentPings.putExtra(UserDetailsPingsActivity.EXTRA_USER_PARCEL, mStateHolder.getUser());
                startActivityForResult(intentPings, ACTIVITY_REQUEST_CODE_PINGS);
                return true;
        }
        return super.onOptionsItemSelected(item);
    }

    @Override
    protected void onActivityResult(int requestCode, int resultCode, Intent data)  {
        switch (requestCode) {
            case ACTIVITY_REQUEST_CODE_PINGS:
                if (resultCode == Activity.RESULT_OK) {
                    User user = (User)data.getParcelableExtra(UserDetailsPingsActivity.EXTRA_USER_RETURNED);
                    if (user != null) {
                        mStateHolder.getUser().getSettings().setGetPings(user.getSettings().getGetPings());
                    }
                }
                break;
            case ACTIVITY_REQUEST_CODE_FETCH_IMAGE:
                if (resultCode == Activity.RESULT_OK) {
                    String imagePath = data.getStringExtra(FetchImageForViewIntent.EXTRA_SAVED_IMAGE_PATH_RETURNED);
                    if (mStateHolder.getIsLoggedInUser() && !TextUtils.isEmpty(imagePath)) {
                        Intent intent = new Intent(this, FullSizeImageActivity.class);
                        intent.putExtra(FullSizeImageActivity.INTENT_EXTRA_IMAGE_PATH, imagePath);
                        intent.putExtra(FullSizeImageActivity.INTENT_EXTRA_ALLOW_SET_NEW_PHOTO, true);
                        startActivityForResult(intent, ACTIVITY_REQUEST_CODE_VIEW_AND_SET_IMAGE);
                    }
                }
                break;
            case ACTIVITY_REQUEST_CODE_VIEW_AND_SET_IMAGE:
                if (resultCode == Activity.RESULT_OK) {
                    String imageUrl = data.getStringExtra(FullSizeImageActivity.INTENT_RETURN_NEW_PHOTO_URL);
                    if (!TextUtils.isEmpty(imageUrl)) {
                        mStateHolder.getUser().setPhoto(imageUrl);
                        ensureUserPhoto(mStateHolder.getUser());
                    }
                }
                break;
            case ACTIVITY_FRIEND_REQUEST:
                if (resultCode == Activity.RESULT_OK) {
                        try {
                            Bundle bundle = data.getExtras();
                            int friendsrequestcount = bundle.getInt("friendsrequestcount");
                            mStateHolder.getUser().setFriendRequestCount(friendsrequestcount);
                            int friendsadded = bundle.getInt("friendsadded");
                            mStateHolder.getUser().setFriendCount(mStateHolder.getUser().getFriendCount()+friendsadded);
                            ensureUi();
                        }   catch( Exception ex )   {

                        }
                }
                break;
            case ACTIVITY_NEW_MESSAGE:
                if (resultCode == Activity.RESULT_OK) {
                        try {
                            Bundle bundle = data.getExtras();
                            int newsleft = bundle.getInt("newsleft");
                            mStateHolder.getUser().setMsgCount(newsleft);
                            ensureUi();
                        }   catch( Exception ex )   {

                        }
                }
                break;
        }
    }

    @Override
    protected Dialog onCreateDialog(int id) {
        switch (id) {
            case DIALOG_CONTACTS:
                final UserContactAdapter adapter = new UserContactAdapter(this, mStateHolder.getUser());
                AlertDialog dlgInfo = new AlertDialog.Builder(this)
                  .setTitle(getResources().getString(R.string.user_details_activity_friends_menu_contact))
                  .setAdapter(adapter, new DialogInterface.OnClickListener() {
                      @Override
                      public void onClick(DialogInterface dlg, int pos) {
                          UserContactAdapter.Action action = (UserContactAdapter.Action)adapter.getItem(pos);
                          switch (action.getActionId()) {
                              case UserContactAdapter.Action.ACTION_ID_SMS:
                                  UiUtil.startSmsIntent(UserDetailsActivity.this, mStateHolder.getUser().getPhone());
                                  break;
                              case UserContactAdapter.Action.ACTION_ID_EMAIL:
                                  UiUtil.startEmailIntent(UserDetailsActivity.this, mStateHolder.getUser().getEmail());
                                  break;
                              case UserContactAdapter.Action.ACTION_ID_PHONE:
                                  UiUtil.startDialer(UserDetailsActivity.this, mStateHolder.getUser().getPhone());
                                  break;
                          }
                      }
                  }).create();
                  return dlgInfo;
        }

        return null;
    }

    @Override
    public boolean onKeyDown(int keyCode, KeyEvent event) {
        if (keyCode == KeyEvent.KEYCODE_BACK && mStateHolder.getIsMainPage()) {
                Dialog dialog = new AlertDialog.Builder(this)
                .setTitle(this.getResources().getString(R.string.app_name))
                .setMessage(this.getResources().getString(R.string.exit_note))
                .setPositiveButton(this.getResources().getString(R.string.ok), new DialogInterface.OnClickListener() {
                    public void onClick(DialogInterface dialog, int whichButton) {
                        finish();
                        android.os.Process.killProcess(android.os.Process.myPid());
                    }
                })
                .setNegativeButton(this.getResources().getString(R.string.cancel), new DialogInterface.OnClickListener() {
                    public void onClick(DialogInterface dialog, int whichButton) {
                    }
                }).create();
                dialog.show();
                return true;
        }
        else
            return super.onKeyDown(keyCode, event);
    }

    @Override
    public void onResume() {
        super.onResume();
    }

    private void ensureUi() {

        setTitle(getString(R.string.user_details_activity_label));

        int sdk = UiUtil.sdkVersion();
        View viewProgressBar = findViewById(R.id.userActivityDetailsProgress);
        TextView tvUserName = (TextView)findViewById(R.id.userDetailsActivityUserName);
        TextView tvLastSeen = (TextView)findViewById(R.id.userDetailsActivityHometownOrLastSeen);
        Button btnFriend = (Button)findViewById(R.id.userDetailsActivityFriendButton);

        View viewBadges = findViewById(R.id.userDetailsActivityGeneralBadges);
        TextView tvBadges = (TextView)findViewById(R.id.userDetailsActivityGeneralBadgesValue);
        ImageView ivBadgesChevron = (ImageView)findViewById(R.id.userDetailsActivityGeneralBadgesChevron);
        View viewScores = findViewById(R.id.userDetailsActivityGeneralScores);
        TextView tvScores = (TextView)findViewById(R.id.userDetailsActivityGeneralScoresValue);
        ImageView ivScoresChevron = (ImageView)findViewById(R.id.userDetailsActivityGeneralScoresChevron);
        View viewMsg = findViewById(R.id.userDetailsActivityGeneralMsg);
        TextView tvMsg = (TextView)findViewById(R.id.userDetailsActivityGeneralMsgValue);
        ImageView ivMsgChevron = (ImageView)findViewById(R.id.userDetailsActivityGeneralMsgChevron);
        View viewFriendRequests = findViewById(R.id.userDetailsActivityFriendRequests);
        View viewCheckins = findViewById(R.id.userDetailsActivityCheckins);
        View viewAddFriends = findViewById(R.id.userDetailsActivityAddFriends);
        View viewFriends = findViewById(R.id.userDetailsActivityFriends);
        View viewTrips = findViewById(R.id.userDetailsActivityTrips);
        View viewSecret = findViewById(R.id.userDetailsActivitySecret);
        TextView tvSecret = (TextView)findViewById(R.id.userDetailsActivitySecretText);
        TextView tvCheckins = (TextView)findViewById(R.id.userDetailsActivityCheckinsText);
        TextView tvFriendRequests = (TextView)findViewById(R.id.userDetailsActivityFriendRequestsText);
        ImageView ivFriendRequestsChevron = (ImageView)findViewById(R.id.userDetailsActivityFriendRequestsChevron);
        ImageView ivCheckinsChevron = (ImageView)findViewById(R.id.userDetailsActivityCheckinsChevron);
        TextView tvFriends = (TextView)findViewById(R.id.userDetailsActivityFriendsText);
        ImageView ivFriends = (ImageView)findViewById(R.id.userDetailsActivityFriendsChevron);
        PhotoStrip psFriends = (PhotoStrip)findViewById(R.id.userDetailsActivityFriendsPhotos);
        TextView tvTrips = (TextView)findViewById(R.id.userDetailsActivityTripsText);

        tvUserName.setText("");
        tvLastSeen.setText("");
        viewBadges.setFocusable(false);
        viewScores.setFocusable(false);
        viewMsg.setFocusable(false);
        tvBadges.setText("0");
        tvMsg.setText("0");
        tvScores.setText("0");
        viewProgressBar.setVisibility(View.VISIBLE);
        btnFriend.setVisibility(View.INVISIBLE);
        ivScoresChevron.setVisibility(View.INVISIBLE);
        ivMsgChevron.setVisibility(View.INVISIBLE);
        ivBadgesChevron.setVisibility(View.INVISIBLE);

        viewTrips.setFocusable(false);
        viewSecret.setFocusable(false);
        viewCheckins.setFocusable(false);
        viewAddFriends.setFocusable(false);
        viewFriendRequests.setFocusable(false);
        viewFriends.setFocusable(false);
        viewCheckins.setVisibility(View.GONE);
        viewTrips.setVisibility(View.GONE);
        viewAddFriends.setVisibility(View.GONE);
        viewFriends.setVisibility(View.GONE);
        viewSecret.setVisibility(View.GONE);
        viewFriendRequests.setVisibility(View.GONE);
        ivCheckinsChevron.setVisibility(View.INVISIBLE);
        ivFriendRequestsChevron.setVisibility(View.INVISIBLE);
        ivFriends.setVisibility(View.INVISIBLE);
        psFriends.setVisibility(View.GONE);
        tvFriendRequests.setText("");
        tvCheckins.setText("");
        tvFriends.setText("");

        if (mStateHolder.getLoadType() >= LOAD_TYPE_USER_PARTIAL) {
            User user = mStateHolder.getUser();

            ensureUserPhoto(user);

            tvUserName.setText(user.getUserName());

            if (mStateHolder.getLoadType() >= LOAD_TYPE_USER_FULL) {
                viewProgressBar.setVisibility(View.GONE);

                if (mStateHolder.getIsLoggedInUser() ||
                        UserUtils.isFriend(user) ||
                        UserUtils.isFriendStatusPendingThem(user) ||
                        UserUtils.isFriendStatusFollowingThem(user)) {
                        btnFriend.setVisibility(View.INVISIBLE);
                     } else if (UserUtils.isFriendStatusPendingYou(user)) {
                         btnFriend.setVisibility(View.VISIBLE);
                         btnFriend.setText(getString(R.string.user_details_activity_friend_confirm));
                         btnFriend.setOnClickListener(new OnClickListener() {
                             @Override
                             public void onClick(View view) {
                                 mStateHolder.startTaskFriend(UserDetailsActivity.this, StateHolder.TASK_FRIEND_ACCEPT);
                             }
                         });
                     } else {
                         btnFriend.setVisibility(View.VISIBLE);
                         btnFriend.setText(getString(R.string.user_details_activity_friend_add));
                         btnFriend.setOnClickListener(new OnClickListener() {
                             @Override
                             public void onClick(View view) {
                                 view.setEnabled(false);
                                 mStateHolder.startTaskFriend(UserDetailsActivity.this, StateHolder.TASK_FRIEND_ADD);
                             }
                         });
                     }
                viewTrips.setVisibility(View.VISIBLE);
                viewTrips.setOnClickListener(new OnClickListener() {
                    @Override
                    public void onClick(View v) {
                        Intent intent = new Intent(UserDetailsActivity.this, UserTripsActivity.class);
                        intent.putExtra(UserTripsActivity.EXTRA_USER_ID, mStateHolder.getUser().getId());
                        startActivity(intent);
                    }
                });
                viewSecret.setVisibility(View.VISIBLE);
                viewSecret.setOnClickListener(new OnClickListener() {
                    @Override
                    public void onClick(View v) {
                        Intent intent = new Intent(UserDetailsActivity.this, SecretActivity.class);
                        intent.putExtra(SecretActivity.EXTRA_USER_ID, mStateHolder.getUser().getId());
                        startActivity(intent);
                    }
                });

                if (mStateHolder.getIsLoggedInUser())   {
                    tvTrips.setText(R.string.user_details_activity_trips_me);
                    tvSecret.setText(getResources().getString(R.string.user_details_activity_self_secret));
                    tvMsg.setText(String.valueOf(user.getMsgCount()));
                    viewMsg.setOnClickListener(new OnClickListener() {
                        @Override
                        public void onClick(View v) {
                            startMsgsActivity();
                        }
                    });
                    viewMsg.setFocusable(true);
                    if (sdk > 3) {
                        ivMsgChevron.setVisibility(View.VISIBLE);
                    }
                } else {
                    tvTrips.setText(R.string.user_details_activity_trips_ta);
                    tvSecret.setText(getResources().getString(R.string.user_details_activity_ta_secret));
                    viewMsg.setVisibility(View.GONE);
                }


                if (user.getCheckin() != null && user.getCheckin().getVenue() != null) {
                    String fixed = getResources().getString(R.string.user_details_activity_last_seen);
                    String full = fixed + " " + user.getCheckin().getVenue().getName();
                    CharacterStyle bold = new StyleSpan(Typeface.BOLD);
                    SpannableString ss = new SpannableString(full);
                    ss.setSpan(bold, fixed.length(), full.length(), Spannable.SPAN_INCLUSIVE_EXCLUSIVE);
                    tvLastSeen.setText(ss);
                    tvLastSeen.setText(user.getHometown());
                    tvLastSeen.setOnClickListener(new OnClickListener() {
                        @Override
                        public void onClick(View view) {
                            startVenueActivity();
                        }
                    });
                }

                if (user.getBadges() != null && user.getBadges().size() > 0) {
                    tvBadges.setText(String.valueOf(user.getBadges().size()));
                    viewBadges.setOnClickListener(new OnClickListener() {
                        @Override
                        public void onClick(View v) {
                            startBadgesActivity();
                        }
                    });
                    viewBadges.setFocusable(true);
                    if (sdk > 3) {
                        ivBadgesChevron.setVisibility(View.VISIBLE);
                    }
                }

                if (user.getScore() > 0) {
                    tvScores.setText(String.valueOf(user.getScore()));
                    viewScores.setOnClickListener(new OnClickListener() {
                        @Override
                        public void onClick(View v) {
                            startScoreActivity();
                        }
                    });
                    viewScores.setFocusable(true);
                    if (sdk > 3) {
                        ivScoresChevron.setVisibility(View.VISIBLE);
                    }
                }

                if (user.getFriendRequestCount() > 0){
                    viewFriendRequests.setVisibility(View.VISIBLE);
                    tvFriendRequests.setText(String.format(getString(R.string.user_details_activity_friend_requests), user.getFriendRequestCount()));
                    viewFriendRequests.setOnClickListener(new OnClickListener() {
                        @Override
                        public void onClick(View v) {
                            startFriendRequestsActivity();
                        }
                    });
                    viewFriendRequests.setFocusable(true);
                    ivFriendRequestsChevron.setVisibility(View.VISIBLE);
                }

                // The rest of the items depend on if we're viewing ourselves or not.
                if ( mStateHolder.getIsLoggedInUser() ) {
                    viewAddFriends.setVisibility(View.VISIBLE);
                    viewAddFriends.setOnClickListener(new OnClickListener() {
                        @Override
                        public void onClick(View v) {
                            startAddFriendsActivity();
                        }
                    });
                    viewAddFriends.setFocusable(true);

                    tvCheckins.setText(
                        user.getCheckinCount() == 1 ?
                            getResources().getString(
                                R.string.user_details_activity_checkins_text_single, user.getCheckinCount()) :
                            getResources().getString(
                                R.string.user_details_activity_checkins_text_plural, user.getCheckinCount()));
                    if (user.getCheckinCount() > 0) {
                        viewCheckins.setVisibility(View.VISIBLE);
                        ivCheckinsChevron.setVisibility(View.VISIBLE);
                        viewCheckins.setOnClickListener(new OnClickListener() {
                            @Override
                            public void onClick(View v) {
                                startCheckinsActivity();
                            }
                        });
                        viewCheckins.setFocusable(true);
                    }
                    
                    if (user.getFriendCount() > 0) {
                        viewFriends.setVisibility(View.VISIBLE);
                        tvFriends.setText(getResources().getString(
                                R.string.user_details_activity_friends_text_self,
                                user.getFriendCount()));
                        viewFriends.setOnClickListener(new OnClickListener() {
                            @Override
                            public void onClick(View v) {
                                startFriendsActivity();
                            }
                        });
                    }
                } else {    // isUserself == false             
                    if ( user.getFriendCount() > 0 )    {
                        viewFriends.setVisibility(View.VISIBLE);
                        tvFriends.setText(getResources().getString(
                                    R.string.user_details_activity_friends_text_ta,
                                    user.getFriendCount()));

                        int friendsInCommon = user.getFriendsInCommon() == null ? 0 :
                            user.getFriendsInCommon().size();
                        if (friendsInCommon > 0) {
                            tvFriends.setText(tvFriends.getText().toString() +
                                (getResources().getString(
                                         R.string.user_details_activity_friends_in_common_text,
                                         friendsInCommon)));
                        }
                        viewFriends.setOnClickListener(new OnClickListener() {
                            @Override
                            public void onClick(View v) {
                                startFriendsInCommonActivity();
                            }
                        });
                        viewFriends.setFocusable(true);
                        ivFriends.setVisibility(View.VISIBLE);
                        if (friendsInCommon > 0) {
                            psFriends.setVisibility(View.VISIBLE);
                            psFriends.setUsersAndRemoteResourcesManager(user.getFriendsInCommon(), mRrm);
                        }
                    }
                }
            } else {
                // Haven't done a full load.
                if (mStateHolder.getRanOnce()) {
                    viewProgressBar.setVisibility(View.GONE);
                }
            }
        } else {
            // Haven't done a full load.
            if (mStateHolder.getRanOnce()) {
                viewProgressBar.setVisibility(View.GONE);
            }
        }

        // Regardless of load state, if running a task, show titlebar progress bar.
        if (mStateHolder.getIsTaskRunning()) {
            setProgressBarIndeterminateVisibility(true);
        } else {
            setProgressBarIndeterminateVisibility(false);
        }

        // Disable friend button if running friend task.
        if (mStateHolder.getIsRunningFriendTask()) {
            btnFriend.setEnabled(false);
        } else {
            btnFriend.setEnabled(true);
        }
    }

    private void ensureUserPhoto(User user) {
        ImageView ivUserPhoto = (ImageView)findViewById(R.id.userDetailsActivityPhoto);
        if (user == null || user.getPhoto() == null) {
            ivUserPhoto.setImageResource(R.drawable.blank_girl);
            return;
        }

        Uri uriPhoto = Uri.parse(user.getPhoto());
        if (mRrm.exists(uriPhoto)) {
            try {
                Bitmap bitmap = BitmapFactory.decodeStream(mRrm.getInputStream(uriPhoto));
                ivUserPhoto.setImageBitmap(bitmap);
            } catch (IOException ex) {
                setUserPhotoMissing(ivUserPhoto, user);
            }
        } else {
            mRrm.request(uriPhoto);
            setUserPhotoMissing(ivUserPhoto, user);
        }

        ivUserPhoto.postInvalidate();
        ivUserPhoto.setOnClickListener(new OnClickListener() {
            @Override
            public void onClick(View v) {
                if (mStateHolder.getLoadType() == LOAD_TYPE_USER_FULL) {
                    User user = mStateHolder.getUser();

                    // If we're viewing our own page, clicking the thumbnail should send the user
                    // to our built-in image viewer. Here we can give them the option of setting
                    // a new photo for themselves.
                    Intent intent = new Intent(UserDetailsActivity.this, FetchImageForViewIntent.class);
                    intent.putExtra(FetchImageForViewIntent.IMAGE_URL, user.getPhoto());
                    intent.putExtra(FetchImageForViewIntent.PROGRESS_BAR_MESSAGE, getResources()
                            .getString(R.string.user_activity_fetch_full_image_message));

                    if (mStateHolder.getIsLoggedInUser()) {
                        intent.putExtra(FetchImageForViewIntent.LAUNCH_VIEW_INTENT_ON_COMPLETION, false);
                        startActivityForResult(intent, ACTIVITY_REQUEST_CODE_FETCH_IMAGE);
                    } else {
                        startActivity(intent);
                    }
                }
            }
        });
    }

    private void setUserPhotoMissing(ImageView ivPhoto, User user) {
        if (PlayLv2Api.MALE.equals(user.getGender())) {
            ivPhoto.setImageResource(R.drawable.blank_boy);
        } else {
            ivPhoto.setImageResource(R.drawable.blank_girl);
        }
    }

    private void startBadgesActivity() {
        if (mStateHolder.getUser() != null) {
            Intent intent = new Intent(UserDetailsActivity.this, BadgesActivity.class);
            intent.putParcelableArrayListExtra(BadgesActivity.EXTRA_BADGE_ARRAY_LIST_PARCEL,
                    mStateHolder.getUser().getBadges());
            intent.putExtra(BadgesActivity.EXTRA_USER_NAME, mStateHolder.getUser().getUserName());
            startActivity(intent);
        }
    }

    private void startMsgsActivity() {
        if (mStateHolder.getUser() != null) {
            Intent intent = new Intent(UserDetailsActivity.this, MessageActivity.class);
            intent.putExtra(MessageActivity.EXTRA_NEW_MESSAGE_COUNT,
                    mStateHolder.getUser().getMsgCount());
            startActivityForResult(intent, ACTIVITY_NEW_MESSAGE);
        }
    }

    private void startScoreActivity() {
        if (mStateHolder.getUser() != null) {
            Intent intent = new Intent(UserDetailsActivity.this, BadgesActivity.class);
            intent.putParcelableArrayListExtra(BadgesActivity.EXTRA_BADGE_ARRAY_LIST_PARCEL,
                    mStateHolder.getUser().getBadges());
            intent.putExtra(BadgesActivity.EXTRA_USER_NAME, mStateHolder.getUser().getUserName());
            startActivity(intent);
        }
    }

    private void startFriendRequestsActivity() {
        Intent intent = new Intent(this, FriendRequestsActivity.class);
        intent.putExtra(FriendRequestsActivity.EXTRA_FRIENDS_REQUEST_COUNT,
                mStateHolder.getUser().getFriendRequestCount());
        startActivityForResult(intent, ACTIVITY_FRIEND_REQUEST);
    }

    private void startCheckinsActivity() {
        Intent intent = new Intent(UserDetailsActivity.this, UserHistoryActivity.class);
        intent.putExtra(UserHistoryActivity.EXTRA_USER_NAME, mStateHolder.getUser().getUserName());
        startActivity(intent);
    }

    private void startFriendsActivity() {
        User user = mStateHolder.getUser();
        Intent intent = null;
        intent = new Intent(UserDetailsActivity.this, UserDetailsFriendsActivity.class);
        intent.putExtra(UserDetailsFriendsActivity.EXTRA_USER_ID, user.getId());
        intent.putExtra(UserDetailsFriendsActivity.EXTRA_USER_NAME, user.getUserName());
        startActivity(intent);
    }

    private void startAddFriendsActivity() {
        startActivity(new Intent(UserDetailsActivity.this, AddFriendsActivity.class));
    }

    private void startFriendsInCommonActivity() {
        User user = mStateHolder.getUser();

        Intent intent = null;
        if (user.getFriendsInCommon() != null && user.getFriendsInCommon().size() > 0) {
            intent = new Intent(UserDetailsActivity.this, UserDetailsFriendsInCommonActivity.class);
            intent.putExtra(UserDetailsFriendsInCommonActivity.EXTRA_USER_PARCEL, user);
        } else {
            intent = new Intent(UserDetailsActivity.this, UserDetailsFriendsActivity.class);
            intent.putExtra(UserDetailsFriendsActivity.EXTRA_USER_ID, user.getId());
            intent.putExtra(UserDetailsFriendsActivity.EXTRA_USER_NAME, user.getUserName());
        }
        startActivity(intent);
    }

    private void startVenueActivity() {
        User user = mStateHolder.getUser();
        if (user.getCheckin() != null &&
            user.getCheckin().getVenue() != null) {
            Intent intent = new Intent(this, VenueActivity.class);
            intent.putExtra(VenueActivity.INTENT_EXTRA_VENUE_PARTIAL, user.getCheckin().getVenue());
            startActivity(intent);
        }
    }

    private void onUserDetailsTaskComplete(User user, Exception ex) {
        mStateHolder.setIsRunningUserDetailsTask(false);
        mStateHolder.setRanOnce(true);
        if (user != null) {
            mStateHolder.setUser(user);
            mStateHolder.setLoadType(LOAD_TYPE_USER_FULL);
        } else if (ex != null) {
            NotificationsUtil.ToastReasonForFailure(this, ex);
        } else {
            Toast.makeText(this, R.string.error_unknow, Toast.LENGTH_LONG).show();
        }

        ensureUi();
    }

    /**
     * Even if the caller supplies us with a User object parcelable, it won't
     * have all the badge etc extra info in it. As soon as the activity starts,
     * we launch this task to fetch a full user object, and merge it with
     * whatever is already supplied in mUser.
     */
    private static class UserDetailsTask extends AsyncTask<String, Void, User> {

        private UserDetailsActivity mActivity;
        private Exception mReason;

        public UserDetailsTask(UserDetailsActivity activity) {
            mActivity = activity;
        }

        @Override
        protected void onPreExecute() {
            mActivity.ensureUi();
        }

        @Override
        protected User doInBackground(String... params) {
            try {
                return ((PlayLv2) mActivity.getApplication()).getPlayLv2Api().user(params[0]);
            } catch (Exception ex) {
                mReason = ex;
                return null;
            }
        }

        @Override
        protected void onPostExecute(User user) {
            if (mActivity != null) {
                mActivity.onUserDetailsTaskComplete(user, mReason);
            }
        }

        @Override
        protected void onCancelled() {
            if (mActivity != null) {
                mActivity.onUserDetailsTaskComplete(null, mReason);
            }
        }

        public void setActivity(UserDetailsActivity activity) {
            mActivity = activity;
        }
    }

    private void onFriendTaskComplete(Response response, int action, Exception ex) {
        mStateHolder.setIsRunningFriendTask(false);

        // The api isn't returning an updated friend status flag here, so we'll
        // overwrite it manually for now, assuming success if the user object
        // was not null.
        User userCurrent = mStateHolder.getUser();
        if (response != null && response.getValue().equals("0")) {
            switch (action) {
                case StateHolder.TASK_FRIEND_ACCEPT:
                    userCurrent.setFriendstatus("1");
                    break;
                case StateHolder.TASK_FRIEND_ADD:
                    userCurrent.setFriendstatus("2");
                    Toast.makeText(UserDetailsActivity.this, getString(R.string.user_details_activity_friend_add_result),
                            Toast.LENGTH_LONG).show();
                    break;
            }
        } else {
            NotificationsUtil.ToastReasonForFailure(this, ex);
        }

        ensureUi();
    }

    private static class FriendTask extends AsyncTask<Void, Void, Response> {

        private UserDetailsActivity mActivity;
        private String mUserId;
        private int mAction;
        private Exception mReason;

        public FriendTask(UserDetailsActivity activity, String userId, int action) {
            mActivity = activity;
            mUserId = userId;
            mAction = action;
        }

        @Override
        protected void onPreExecute() {
            mActivity.ensureUi();
        }

        @Override
        protected Response doInBackground(Void... params) {
            PlayLv2Api playlv2api = ((PlayLv2) mActivity.getApplication()).getPlayLv2Api();
            Response response = null;
            try {
                switch (mAction) {
                    case StateHolder.TASK_FRIEND_ACCEPT:
                        {
                            response = playlv2api.friendApprove(mUserId);
                            if (response == null)  response = new Response();
                            return response;
                        }
                    case StateHolder.TASK_FRIEND_ADD:
                        {
                            response = playlv2api.friendSendrequest(mUserId);
                            if (response == null)  response = new Response();
                            return response;
                        }
                    default:
                        throw new PlayLv2ApiException("Unknown action type supplied.");
                }
            } catch (Exception ex) {
                mReason = ex;
                return null;
            }
        }

        @Override
        protected void onPostExecute(Response response) {
            if (mActivity != null) {
                mActivity.onFriendTaskComplete(response, mAction, mReason);
            }
        }

        @Override
        protected void onCancelled() {
            if (mActivity != null) {
                mActivity.onFriendTaskComplete(null, mAction, mReason);
            }
        }

        public void setActivity(UserDetailsActivity activity) {
            mActivity = activity;
        }
    }

    private static class StateHolder {
        public static final int TASK_FRIEND_ACCEPT = 0;
        public static final int TASK_FRIEND_ADD = 1;

        private User mUser;
        private boolean mIsLoggedInUser;
        private UserDetailsTask mTaskUserDetails;
        private boolean mIsRunningUserDetailsTask;
        private boolean mRanOnce;
        private int mLoadType;
        private boolean mIsMainPage;
        private FriendTask mTaskFriend;
        private boolean mIsRunningFriendTask;

        public StateHolder() {
            mIsRunningUserDetailsTask = false;
            mIsRunningFriendTask = false;
            mIsLoggedInUser = false;
            mRanOnce = false;
            mIsMainPage = false;
            mLoadType = LOAD_TYPE_USER_NONE;
        }

        public boolean getIsMainPage()  {
            return mIsMainPage;
        }

        public void setIsMainPage(boolean isMainPage)   {
            mIsMainPage = isMainPage;
        }

        public boolean getIsLoggedInUser() {
            return mIsLoggedInUser;
        }

        public void setIsLoggedInUser(boolean isLoggedInUser) {
            mIsLoggedInUser = isLoggedInUser;
        }

        public User getUser() {
            return mUser;
        }

        public void setUser(User user) {
            mUser = user;
        }

        public int getLoadType() {
            return mLoadType;
        }

        public void setLoadType(int loadType) {
            mLoadType = loadType;
        }

        public void startTaskUserDetails(UserDetailsActivity activity, String viewUserId) {
            if (!mIsRunningUserDetailsTask) {
                mIsRunningUserDetailsTask = true;
                mTaskUserDetails = new UserDetailsTask(activity);
                mTaskUserDetails.execute(viewUserId);
            }
        }

        public void startTaskFriend(UserDetailsActivity activity, int action) {
            if (!mIsRunningFriendTask) {
                mIsRunningFriendTask = true;
                mTaskFriend = new FriendTask(activity, mUser.getId(), action);
                mTaskFriend.execute();
            }
        }

        public boolean getIsRunningUserDetailsTask() {
            return mIsRunningUserDetailsTask;
        }

        public void setIsRunningUserDetailsTask(boolean isRunning) {
            mIsRunningUserDetailsTask = isRunning;
        }

        public boolean getRanOnce() {
            return mRanOnce;
        }

        public void setRanOnce(boolean ranOnce) {
            mRanOnce = ranOnce;
        }

        public boolean getIsRunningFriendTask() {
            return mIsRunningFriendTask;
        }

        public void setIsRunningFriendTask(boolean isRunning) {
            mIsRunningFriendTask = isRunning;
        }

        public void cancelTasks() {
            if (mTaskUserDetails != null) {
                mTaskUserDetails.setActivity(null);
                mTaskUserDetails.cancel(true);
            }
            if (mTaskFriend != null) {
                mTaskFriend.setActivity(null);
                mTaskFriend.cancel(true);
            }
        }

        public boolean getIsTaskRunning() {
            return mIsRunningUserDetailsTask || mIsRunningFriendTask;
        }
    }

    private class RemoteResourceManagerObserver implements Observer {
        @Override
        public void update(Observable observable, Object data) {
            mHandler.post(mRunnableUpdateUserPhoto);
        }
    }

    private Runnable mRunnableUpdateUserPhoto = new Runnable() {
        @Override
        public void run() {
            ensureUserPhoto(mStateHolder.getUser());
        }
    };
}
